package im.composer.io;

import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.channels.SeekableByteChannel;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.StandardOpenOption;

import org.tritonus.share.sampled.file.TDataOutputStream;

public class RandomPathWriter implements TDataOutputStream {

	private final SeekableByteChannel channel;

	public RandomPathWriter(Path path) throws IOException {
		Files.createDirectories(path.getParent());
		channel = Files.newByteChannel(path, StandardOpenOption.CREATE, StandardOpenOption.WRITE);
		channel.position(0);
	}

	@Override
	public void write(int b) throws IOException {
		channel.write(ByteBuffer.wrap(new byte[] { (byte) (b & 0xFF) }));
	}

	@Override
	public void write(byte[] b) throws IOException {
		channel.write(ByteBuffer.wrap(b));
	}

	@Override
	public void write(byte[] b, int off, int len) throws IOException {
		channel.write(ByteBuffer.wrap(b,off,len));
	}

	@Override
	public void writeBoolean(boolean v) throws IOException {
        write(v ? 1 : 0);
	}

	@Override
	public void writeByte(int v) throws IOException {
		write(v);
	}

	@Override
	public void writeShort(int v) throws IOException {
        write((v >>> 8) & 0xFF);
        write((v >>> 0) & 0xFF);
	}

	@Override
	public void writeChar(int v) throws IOException {
        write((v >>> 8) & 0xFF);
        write((v >>> 0) & 0xFF);
	}

	@Override
	public void writeInt(int v) throws IOException {
        write((v >>> 24) & 0xFF);
        write((v >>> 16) & 0xFF);
        write((v >>>  8) & 0xFF);
        write((v >>>  0) & 0xFF);
	}

	@Override
	public void writeLong(long v) throws IOException {
        write((int)(v >>> 56) & 0xFF);
        write((int)(v >>> 48) & 0xFF);
        write((int)(v >>> 40) & 0xFF);
        write((int)(v >>> 32) & 0xFF);
        write((int)(v >>> 24) & 0xFF);
        write((int)(v >>> 16) & 0xFF);
        write((int)(v >>>  8) & 0xFF);
        write((int)(v >>>  0) & 0xFF);
	}

	@Override
	public void writeFloat(float v) throws IOException {
        writeInt(Float.floatToIntBits(v));
	}

	@Override
	public void writeDouble(double v) throws IOException {
        writeLong(Double.doubleToLongBits(v));
	}

	@Override
	public void writeBytes(String s) throws IOException {
		write(s.getBytes());
	}

	@Override
	public void writeChars(String s) throws IOException {
        int clen = s.length();
        int blen = 2*clen;
        byte[] b = new byte[blen];
        char[] c = new char[clen];
        s.getChars(0, clen, c, 0);
        for (int i = 0, j = 0; i < clen; i++) {
            b[j++] = (byte)(c[i] >>> 8);
            b[j++] = (byte)(c[i] >>> 0);
        }
        write(b, 0, blen);
	}

	@Override
	public void writeUTF(String s) throws IOException {
		write(s.getBytes("UTF-8"));
	}

	@Override
	public boolean supportsSeek() {
		return true;
	}

	@Override
	public void seek(long position) throws IOException {
		channel.position(position);
	}

	@Override
	public long getFilePointer() throws IOException {
		throw new UnsupportedOperationException();
	}

	@Override
	public long length() throws IOException {
		return channel.size();
	}

	@Override
	public void writeLittleEndian32(int value) throws IOException {
		writeByte(value & 0xFF);
		writeByte((value >> 8) & 0xFF);
		writeByte((value >> 16) & 0xFF);
		writeByte((value >> 24) & 0xFF);
	}

	@Override
	public void writeLittleEndian16(short value) throws IOException {
		writeByte(value & 0xFF);
		writeByte((value >> 8) & 0xFF);
	}

	@Override
	public void close() throws IOException {
		channel.close();
	}

}
